package awesome.cpu.structure;

import java.util.ArrayList;
import java.util.HashMap;

import awesome.cpu.control.CentralProcessorUnit;
import awesome.cpu.utils.Mode;

public class Control extends Component{
	
	private static boolean outputDebug = false;
	
	private final static Control mInstance = new Control();
	
	private Control(){}
	
	public static Control getInstance(){
		return mInstance;
	}
	
	public final static int KEY_OPERATION = 20;
	private int mOperation;
	private int bc;
	
	public final static int KEY_PC_WRITE_CONDITION = 0;
	private int mPCWriteCond;
	
	public final static int KEY_PC_WRITE = 1;
	private int mPCWrite;
	
	public final static int KEY_I_OR_D = 9;
	private int mIORD;
	
	public final static int KEY_MEM_READ = 10;
	private int mMemRead;
	
	public final static int KEY_MEM_WRITE = 11;
	private int mMemWrite;
	
	public final static int KEY_MEM_TO_REG = 12;
	private int mMemToReg;
	
	public final static int KEY_IR_WRITE = 13;
	private int mIRWrite;
	
	public final static int KEY_IMM_SRC = 14;
	private int mImmSrc;
	
	public final static int KEY_FLAG_WRITE = 2;
	private int mFlagWrite;
		
	public final static int KEY_PC_SOURCE = 3;
	private int mPCSource;
	
	public final static int KEY_ALU_OP = 8;
	private int mALUOp;
	
	public final static int KEY_ALU_SRC_B = 5;
	private int mALUSrcB;
	
	public final static int KEY_ALU_SRC_A = 4;
	private int mALUSrcA;
	
	public final static int KEY_REG_WRITE = 6;
	private int mRegWrite;
	
	public final static int KEY_REG_DST = 7;
	private int mRegDst;
	
	public final static int KEY_FUNCTION = 16;
	private int mFunction;
	
	public final static int KEY_PCREG_WRITE = 15;
	private int mPCRegWrite;
	
	private int preciseStatus;
	
	//信号量和下一状态的矩阵，对应编号的15个信号量，若是dc则值为-1。
	//最后一个数>=0时表示下一个状态的标号，若为-1表示需要通过operation来算出下一个状态。
	private int signalMatrix[][] = {
			
			//=====================================通用取指和译码周期===========================================
			{14, 1, 0, 0, 0, 1, 0, -1, 0, 0, 2, 0, -1, 1, -1, 1,        1},//0 取指			
			{14, 0, 0, -1, 0, 3, 0, -1, 0, -1, 0, 0, -1, 0, 1, 0,      -1},//1 译码
			
			//=======================第一类中的ADD|SUB|MUL|AND|OR的执行及写回的周期===============================			
			{14, 0, 0, -1, 1, 0, 0, -1, 5, -1, 0, 0, -1, 0, -1, 0,      6},//2 执行 寄存器ADD指令
			{14, 0, 0, -1, 1, 0, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,      6},//3 执行 寄存器SUB指令
			{14, 0, 0, -1, 1, 0, 0, -1, 5, -1, 0, 0, -1, 0, -1, 0,      6},//4 执行 寄存器MUL/寄存器AND指令
			{14, 0, 0, -1, 1, 0, 0, -1, 3, -1, 0, 0, -1, 0, -1, 0,      6},//5 执行 寄存器OR指令
			{14, 0, 0, -1, -1, -1, 1, 1, -1, -1, 0, 0, 0, 0, -1, 0,     0},//6 写回
			
			//==================================第一类中的MOV的写回的周期========================================
			{14, 0, 0, -1, -1, 0, 1, 1, -1, -1, 0, 0, 3, 0, -1, 0,      0},//7 写回

			//===========================第二类中的ADD|SUB|AND|OR的执行及写回的周期===============================
			{14, 0, 0, -1, 1, 2, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0,     12}, //8 ADD
			{14, 0, 0, -1, 1, 2, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,     12}, //9 SUB
			{14, 0, 0, -1, 1, 2, 0, -1, 2, -1, 0, 0, -1, 0, -1, 0,     12}, //10 AND
			{14, 0, 0, -1, 1, 2, 0, -1, 3, -1, 0, 0, -1, 0, -1, 0,     12}, //11 OR
			{14, 0, 0, -1, -1,-1, 1, 1, -1, -1, 0, 0, 0, 0, -1, 0,      0}, //12 写回
			
			//==================================第二类中的MOV的写回的周期========================================
			{14, 0, 0, -1, -1, 2, 1, 1, -1, -1, 0, 0, 3, 0, -1, 0,      0}, //13 写回
			
			//==========================第一类中的LDB+的执行，LDB[+/-]访存、写回周期===============================
			{14, 0, 0, -1, 1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0,     15}, //14 执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 1, 0, -1, 0, -1, 0,   16}, //15 访存
			{14, 0, 0, -1, -1, -1, 1, 1, -1, -1, 0, 0, 1, 0, -1, 0,     0}, //16 写回
			
			//==============================第一类中的STW+的执行，STW[+/-]访存周期================================
			{14, 0, 0, -1, 1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0,     18}, //17 执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 0, 2, -1, 0, -1, 0,    0}, //18 访存


			//===================================第一类中的CMPSUB.A的执行周期====================================
			{14, 0, 1, -1, 1, 0, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,      0}, //19 执行

			//======================================第一类中的JUMP写回周期=======================================
			{14, 1, 0, 2, -1, -1, 0, -1, -1, -1, 0, 0, -1, 0, -1, 0,    0}, //20 写回
			
			//=====================================第一类中的JUMP.L的执行周期====================================
			{14, 1, 0, 2, -1, -1, 1, 2, -1, -1, 0, 0, 2, 0, -1, 0,      0}, //21 写回

			//=====================================长乘法MULSL的执行|写回周期====================================
			{14, 0, 0, -1, 1, 4, 0, -1, 5, -1, 0, 0, -1, 0, -1, 0,     23}, //22 执行
			{14, 0, 0, -1, -1, -1, 2, 1, -1, -1, 0, 0, 0, 0, -1, 0,     0}, //23 写回
			
			//========================================B指令的写回周期===========================================
			{14, 1, 0, 1, -1, -1, 0, -1, -1, -1, 0, 0, -1, 0, -1, 0,    0}, //24 写回
			
			//===========================第一类LDW+指令的执行，LWD[+/-]的访存|写回周期=============================
			{14, 0, 0, -1, 1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0,     26}, //25 执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 2, 0, -1, 0, -1, 0,   27}, //26 访存
			{14, 0, 0, -1, -1, -1, 1, 1, -1, -1, 0, 0, 1, 0, -1, 0,     0}, //27 写回			
			
			//===================================第三类LDB+指令的执行|访存|写回周期================================
			{14, 0, 0, -1, 1, 3, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0,      29}, //28 执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 1, 0, -1, 0, -1, 0,   30}, //29 访存
			{14, 0, 0, -1, -1, -1, 1, 1, -1, -1, 0, 0, 1, 0, -1, 0,     0}, //30 回写
			
			//=======================================第三类LDW+指令的执行|访存|写回周期============================
			{14, 0, 0, -1, 1, 3, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0,      32}, //31 执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 2, 0, -1, 0, -1, 0,   33}, //32 访存
			{14, 0, 0, -1, -1, -1, 1, 1, -1, -1, 0, 0, 1, 0, -1, 0,     0}, //33 回写

			//===================================第一类中的STB+的执行、STB[+/-]访存周期===========================
			{14, 0, 0, -1, 1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0,     35}, //34 执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 0, 1, -1, 0, -1, 0,    0}, //35 访存		
			
			//========================================Bcc指令的写回周期=========================================
			{-1, 0, 0, 1, -1, -1, 0, -1, -1, -1, 0, 0, -1, 0, -1, 0,    0}, //36 写回		

			//========================================B.L指令的写回周期=========================================
			{14, 1, 0, 1, -1, -1, 1, 2, -1, -1, 0, 0, 4, 0, -1, 0,      0}, //37 写回
			
			//========================================Bcc.L指令的写回周期=======================================
			{-1, 0, 0, 1, -1, -1, 1, 2, -1, -1, 0, 0, 4, 0, -1, 0,      0}, //38 写回 

			//=======================================第三类LDB-指令的执行周期=====================================
			{14, 0, 0, -1, 1, 3, 0, -1, 1, -1, 0, 0, -1, 0, 0, 0,      29}, //39 执行
			
			//=======================================第三类LDW-指令的执行周期=====================================
			{14, 0, 0, -1, 1, 3, 0, -1, 1, -1, 0, 0, -1, 0, 0, 0,      32}, //40 执行
			
			//==================================第三类STW[+/-]指令的执行|访存周期==================================
			{14, 0, 0, -1, 1, 3, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0,     43}, //41 加执行
			{14, 0, 0, -1, 1, 3, 0, -1, 1, -1, 0, 0, -1, 0, 0, 0,     43}, //42 减执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 0, 2, -1, 0, -1, 0,    0}, //43 访存 

			//==================================第三类STB[+/-]指令的执行|访存周期==================================
			{14, 0, 0, -1, 1, 3, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0,     46}, //44 加执行
			{14, 0, 0, -1, 1, 3, 0, -1, 1, -1, 0, 0, -1, 0, 0, 0,     46}, //45 减执行
			{14, 0, 0, -1, -1, -1, 0, -1, -1, 1, 0, 1, -1, 0, -1, 0,    0}, //46 访存
			
			//======================================第一类中的LDB-的执行周期======================================
			{14, 0, 0, -1, 1, 0, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,     15}, //47 执行
			
			//======================================第一类中的STW-的执行周期======================================
			{14, 0, 0, -1, 1, 0, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,     18}, //48 执行
			
			//======================================第一类LDW-指令的执行周期======================================
			{14, 0, 0, -1, 1, 0, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,     26}, //49 执行
			
			//======================================第一类STB-指令的执行周期======================================
			{14, 0, 0, -1, 1, 0, 0, -1, 1, -1, 0, 0, -1, 0, -1, 0,     35}, //50 执行
			
			//+++++++++++++++++++++++++++++++++++++++++++++ END 指令 ++++++++++++++++++++++++++++++++++++++++++
			{14, 0, 0, -1, -1, -1, 0, -1, -1, -1, 0, 0, -1, 0, -1, 0,  51}, //51 结束
  			
	};
	
	private final static HashMap<Integer, Integer> nextStatusMatrix = new HashMap<Integer, Integer>();
	static{

		nextStatusMatrix.put(0x04, 3);
		nextStatusMatrix.put(0x00, 4);
		nextStatusMatrix.put(0x18, 5);
		nextStatusMatrix.put(0x1a, 7);		
		nextStatusMatrix.put(0x28, 8);
		nextStatusMatrix.put(0x24, 9);
		nextStatusMatrix.put(0x20, 10);
		nextStatusMatrix.put(0x38, 11);
		nextStatusMatrix.put(0x3a, 13);
		nextStatusMatrix.put(0x15, 19);
		nextStatusMatrix.put(0x10, 20);	// JUMP
		nextStatusMatrix.put(0x11, 21);	// JUMP.L
		
		nextStatusMatrix.put(0x59, 25); // 第一类LDW+
		nextStatusMatrix.put(0x5c, 34); // 第一类STB+
		nextStatusMatrix.put(0x5d, 14); // 第一类LDB+
		nextStatusMatrix.put(0x58, 17); // 第一类STW+
		
		nextStatusMatrix.put(0x79, 31); // 第三类LDW+		
		nextStatusMatrix.put(0x7d, 28); // 第三类LDB+		
		nextStatusMatrix.put(0x78, 41); // 第三类STW+		
		nextStatusMatrix.put(0x7c, 44); // 第三类STB+
		
		nextStatusMatrix.put(0x71, 40); // 第三类LDW-
		nextStatusMatrix.put(0x74, 45); // 第三类STB-
		nextStatusMatrix.put(0x75, 39); // 第三类LDB-
		nextStatusMatrix.put(0x70, 42); // 第三类STW-
		
		nextStatusMatrix.put(0x55, 47); // 第一类LDB-
		nextStatusMatrix.put(0x51, 49); // 第一类LDW-
		nextStatusMatrix.put(0x54, 50); // 第一类STB-
		nextStatusMatrix.put(0x50, 48); // 第一类STW-
		
		nextStatusMatrix.put(-1, 51); // END
		
	}
	
	private int currentStatus = 0; 
	
	@Override
	int getData(int keyInterface) {
		
		switch(keyInterface){
		case KEY_PC_WRITE_CONDITION:
			return this.mPCWriteCond;
		case KEY_PC_WRITE:
			return this.mPCWrite;
		case KEY_I_OR_D:
			return this.mIORD;
		case KEY_MEM_READ:
			return this.mMemRead;
		case KEY_MEM_WRITE:
			return this.mMemWrite;
		case KEY_MEM_TO_REG:
			return this.mMemToReg;
		case KEY_IR_WRITE:
			return this.mIRWrite;
		case KEY_PC_SOURCE:
			return this.mPCSource;
		case KEY_ALU_OP:
			return this.mALUOp;
		case KEY_ALU_SRC_B:
			return this.mALUSrcB;
		case KEY_ALU_SRC_A:
			return this.mALUSrcA;
		case KEY_REG_WRITE:
			return this.mRegWrite;
		case KEY_REG_DST:
			return this.mRegDst;
		case KEY_IMM_SRC:
			return this.mImmSrc;
		case KEY_FLAG_WRITE:
			return this.mFlagWrite;
		case KEY_PCREG_WRITE:
			return this.mPCRegWrite;
		}
		return 0;
	}

	void setControl() {

		getCurrentSignal();
	}

	@Override
	boolean setData(int keyInterface, int data) {
		
		if (keyInterface == KEY_OPERATION){
			mOperation = data;
			bc = (mOperation & 0x1f) >> 1;			
			return true;
		}else if (keyInterface == KEY_FUNCTION){
			mFunction = data;
			return true;
		}
			
		return false;
	}	

	void getCurrentSignal(){
		
		preciseStatus = currentStatus;
		
		// end?
		if (mOperation == -1){
			
			if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL)
				System.out.println(" --- ENDing --- ");
			
			CentralProcessorUnit.getInstance().makeStop();
			
			return;
		}
		
		// get signals
		mPCWrite 		=	signalMatrix[currentStatus][KEY_PC_WRITE];
		mIORD			=	signalMatrix[currentStatus][KEY_I_OR_D];
		mMemRead		=	signalMatrix[currentStatus][KEY_MEM_READ];
		mMemWrite		=	signalMatrix[currentStatus][KEY_MEM_WRITE];
		mMemToReg		=	signalMatrix[currentStatus][KEY_MEM_TO_REG];
		mIRWrite		=	signalMatrix[currentStatus][KEY_IR_WRITE];
		mImmSrc			=	signalMatrix[currentStatus][KEY_IMM_SRC];
		mFlagWrite		=	signalMatrix[currentStatus][KEY_FLAG_WRITE];
		mPCSource		=	signalMatrix[currentStatus][KEY_PC_SOURCE];		
		mALUSrcB		=	signalMatrix[currentStatus][KEY_ALU_SRC_B];
		mALUSrcA		=	signalMatrix[currentStatus][KEY_ALU_SRC_A];
		mRegWrite		=	signalMatrix[currentStatus][KEY_REG_WRITE];
		mRegDst			=	signalMatrix[currentStatus][KEY_REG_DST];
		mPCRegWrite		=	signalMatrix[currentStatus][KEY_PCREG_WRITE];
		
		// calculate bc code
		mPCWriteCond	= 	signalMatrix[currentStatus][KEY_PC_WRITE_CONDITION];
		if (mPCWriteCond == -1)
			mPCWriteCond = bc;
		
		// calculate ALUop
		mALUOp			=	signalMatrix[currentStatus][KEY_ALU_OP];
		if (mALUOp == 5){
			switch (mOperation){
			case 0x08:
				if (mFunction == 0)
					mALUOp = 0;
				else if (mFunction == 9)
					mALUOp = 4;
				break;
			case 0x00:
				if (mFunction == 0)
					mALUOp = 2;
				else if (mFunction == 9)
					mALUOp = 4;
				break;
			}
		}


		if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL) {
			System.out.println("Control: currentStatus = "+currentStatus);
			//System.out.println("Control: ALUSrcA = " + mALUSrcA);
			//System.out.println("Control: ALUSrcB = " + mALUSrcB);
			System.out.println("Control: ALUOp = " + mALUOp);
			System.out.println("Control: PCWrite = " + mPCWrite);
			System.out.println("Control: PCSrc = " + mPCSource);
			//System.out.println("Control: IorD = " + mIORD);
			//System.out.println("Control: MemoryRead = " + mMemRead);
			//System.out.println("Control: MemoryWrite = " + mMemWrite);
			//System.out.println("Control: PCRegWrite = " + mPCRegWrite);
			System.out.println("Control: PCCond = " + mPCWriteCond);
			//System.out.println("Control: bcCode = " + bc);
		}
		
		stepToNextStatus();

	}
	
	void stepToNextStatus(){
		
		if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL) {			
			System.out.println("Control: operation = "+mOperation);
			System.out.println("Control: bcCode = " + bc);
		}		
		
		if (signalMatrix[currentStatus][16] != -1){//get next status directly
			currentStatus = signalMatrix[currentStatus][16];
		}else if (mOperation == 8){
			if (mFunction == 0)
				currentStatus = 2;//ADD
			else if (mFunction == 9)
				currentStatus = 22;//MULSL
		}else if ((mOperation & 0xe1) == 0xa0){//B or Bcc
			
			if ((mOperation & 0x1c) == 0x1c){
				currentStatus = 24;//B
				if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL)
					System.out.println("Control : instruction type B!");
			}
			else {
				currentStatus = 36;//Bcc
				if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL)
					System.out.println("Control : instruction type Bcc!");
			}
			
		}else if ((mOperation & 0xe1) == 0xa1){//B.L or Bcc.L
			
			if ((mOperation & 0x1c) == 0x1c){
				currentStatus = 37;//B.L
				if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL)
					System.out.println("Control : instruction type B.L!");
			}
			else {
				currentStatus = 38;//Bcc.L
				if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL)
					System.out.println("Control : instruction type Bcc.L!");
			}
			
		}else{
			currentStatus = nextStatusMatrix.get(mOperation);
		}

		return;
		
	}
	
	
	public ArrayList<Integer> getSignals(){
		ArrayList<Integer> signal = new ArrayList<Integer>();
		
		//PCWriteCond
		signal.add(mPCWriteCond);
		
		for (int i = 1; i <= 15; i ++){
			if (i != 8)
				signal.add(signalMatrix[preciseStatus][i]);
			else{//ALUOp
				signal.add(mALUOp);
			}				
		}
		
		return signal;
		
	}
	
	public int getCurrentStatus(){
		return preciseStatus;
	}
	
	public void stop() {
		preciseStatus = mOperation = currentStatus = 0;
	}
}
